#ifndef _TBVEC3_H_
#define _TBVEC3_H_

#include "TBSettings.h"

#include "TBMath.h"

namespace tb
{

	class Vec2;

	class TB_DLL_FUNC Vec3
	{

	public:

		// ================================
		// Static methods
		// ================================

		static Vec3 _RANDOM(float a_Min, float a_Max);

		// ================================
		// Constructors
		// ================================

		Vec3();
		Vec3(float a_X, float a_Y, float a_Z);
		Vec3(float* a_Vector);
		Vec3(const Vec3& a_Copy);
		~Vec3();

		// ================================
		// Conversion
		// ================================

		const Vec3& ToOpenGLVector(float* a_Target) const;
		Vec2 ToXY() const;
		Vec2 ToYX() const;
		Vec2 ToYZ() const;
		Vec2 ToZY() const;
		Vec2 ToZX() const;
		Vec2 ToXZ() const;

		// ================================
		// Assignment & Access
		// ================================

		Vec3& operator = (const Vec3& a_Point);
		Vec3& operator = (float a_Value);
		float& operator [] (int a_Member);
		float operator [] (int a_Member) const;

		// ================================
		// Operators
		// ================================

		Vec3 operator - ();

		Vec3& operator += (const Vec3& a_Point);
		Vec3 operator + (const Vec3& a_Point) const;

		Vec3& operator -= (const Vec3& a_Point);
		Vec3 operator - (const Vec3& a_Point) const;

		Vec3& operator *= (float a_Scalar);
		Vec3& operator *= (const Vec3& a_Point);
		Vec3 operator * (float a_Value) const;
		Vec3 operator * (Vec3& a_Point) const;
		inline friend Vec3 operator * (float a_Value, const Vec3& a_Vector)
		{ 
			return Vec3(
				a_Vector.x * a_Value, 
				a_Vector.y * a_Value, 
				a_Vector.z * a_Value 
			); 
		}

		Vec3& operator /= (float a_Value);
		Vec3& operator /= (const Vec3& a_Point);
		Vec3 operator / (float a_Value) const;
		Vec3 operator / (const Vec3& a_Point) const;

		// ================================
		// Comparisons
		// ================================

		bool operator == (const Vec3& a_Point);
		bool operator == (float a_Value);

		bool operator != (const Vec3& a_Point);
		bool operator != (float a_Value);

		bool operator > (const Vec3& a_Point);
		bool operator > (float a_Value);

		bool operator >= (const Vec3& a_Point);
		bool operator >= (float a_Value);

		bool operator < (const Vec3& a_Point);
		bool operator < (float a_Value);

		bool operator <= (const Vec3& a_Point);
		bool operator <= (float a_Value);

		// ================================
		// Functions
		// ================================

		//! Clears the values of this Vec3.
		Vec3& Clear();

		//! Randomizes this vector between a min and max value
		Vec3& Randomize(float a_Min, float a_Max);

		//! Returns the squared length of this Vec3.
		float GetSquaredLength() const;
		//! Returns the length of this Vec3. NOTE: USE GetSquaredLength WHEREVER POSSIBLE DUE TO SPEED
		float GetLength() const;

		//! Returns the squared distance between this Vec3 and another Vec3.
		float GetSquaredDistanceBetween(const Vec3& a_Other) const;
		//! Returns the distance between this Vec3 and another Vec3. NOTE: USE GetSquaredDistanceBetween WHEREVER POSSIBLE DUE TO SPEED
		float GetDistanceBetween(const Vec3& a_Other) const;

		//! Turns this Vec3 into the normalized version of itself.
		Vec3& Normalize();
		//! Returns the normalized version of this Vec3. NOTE: SLOW
		Vec3 GetNormalized() const;

		//! Get dot product between this and another vector.
		float GetDotProduct(const Vec3& a_Other) const;

		//! Turns this Vec3 into the cross product of this Vec3 and another Vec3.
		Vec3& CrossProduct(const Vec3& a_Other);
		//! Cross product of this Vec3 and another Vec3.
		Vec3 GetCrossProduct(const Vec3& a_Other) const;

		//! Turns this Vec3 in the vector going in the opposite direction.
		Vec3& Reverse();
		//! Returns the vector in the opposite direction of this Vec3.
		Vec3 GetReverse() const;

		Vec3& Reflect(const Vec3& a_Normal);
		Vec3 GetReflected(const Vec3& a_Normal) const;

		//! Turns this Vec3 into the reciprocal (1 / x) of itself.
		Vec3& Reciprocral();
		//! Returns a Vec3 with the reciprocal (1 / x) of this Vec3.
		Vec3 Vec3::GetReciprocal() const;

		//! Returns the halfway vector (the vector BETWEEN two vectors)
		Vec3 GetHalfWayVector(const Vec3& a_Other) const;

		//! Turns this Vec3 into the minimum of itself and another Vec3.
		Vec3& Min(const Vec3& a_Other);
		//! Returns the minimum of this Vec3 and another Vec3.
		Vec3 GetMin(const Vec3& a_Other) const;

		//! Turns this Vec3 into the maximum of itself and another Vec3.
		Vec3& Max(const Vec3& a_Other);
		// Returns the maximum of this Vec3 and another Vec3.
		Vec3 GetMax(const Vec3& a_Other) const;

		//! Clamp vector between minimum and maximum
		Vec3& Clamp(float a_Min, float a_Max);
		Vec3 GetClamped(float a_Min, float a_Max) const;

		// ================================
		// Interpolation
		// ================================

		Vec3& Lerp(const Vec3& a_Other, float a_Amount);
		Vec3 GetLerp(const Vec3& a_Other, float a_Amount) const;

		// Returns the spherical interpolation between this Vec3 and another Vec3 at a_Percentage (between 0 and 1).
		// TODO: Rip working version from Relativity
		Vec3 GetSphericalInterpolationNormalized(const Vec3& a_Other, float a_Percentage);

		// ================================
		// Data
		// ================================

		float x, y, z;

	}; // class Vec3

};

#include "TBVec3.inl"

#endif